package org.ldx.lotterysystem.service.impl;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.ldx.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import org.ldx.lotterysystem.common.exception.ServiceException;
import org.ldx.lotterysystem.common.utils.JacksonUtil;
import org.ldx.lotterysystem.common.utils.RedisUtil;
import org.ldx.lotterysystem.controller.param.CreateActivityParam;
import org.ldx.lotterysystem.controller.param.CreatePrizeByActivityParam;
import org.ldx.lotterysystem.controller.param.CreateUserByActivityParam;
import org.ldx.lotterysystem.controller.param.PageParam;
import org.ldx.lotterysystem.dao.dataobject.ActivityDo;
import org.ldx.lotterysystem.dao.dataobject.ActivityPrizeDo;
import org.ldx.lotterysystem.dao.dataobject.ActivityUserDo;
import org.ldx.lotterysystem.dao.dataobject.PrizeDo;
import org.ldx.lotterysystem.dao.mapper.*;
import org.ldx.lotterysystem.service.ActivityService;
import org.ldx.lotterysystem.service.PrizeService;
import org.ldx.lotterysystem.service.dto.*;
import org.ldx.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import org.ldx.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import org.ldx.lotterysystem.service.enums.ActivityStatusEnum;
import org.ldx.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static java.util.spi.ToolProvider.findFirst;
@Slf4j
@Service
public class ActivityServiceImpl implements ActivityService {
    @Resource
    UserMapper userMapper;
    @Resource
    PrizeMapper prizeMapper;
    @Resource
    ActivityMapper activityMapper;
    @Resource
    ActivityPrizeMapper activityPrizeMapper;
    @Resource
    ActivityUserMapper activityUserMapper;
    @Resource
    RedisUtil redisUtil;
    //redis前缀 方便区分
    private final String ACTIVITY_HEAD = "ACTIVITY_";
    //活动过期时间 三天
    private final Long ACTIVITY_TIMEOUT = 60L*60*24*3;
    @Override
    @Transactional(rollbackFor = Exception.class)//由于涉及到多表  构建事务 出错回滚
    public CreateActivityDto createActivity(CreateActivityParam param) {
        //校验信息是否正确
        checkActivityInfo(param);

        //保存活动信息
        ActivityDo activityDo = new ActivityDo();

        activityDo.setActivityName(param.getActivityName());
        activityDo.setDescription(param.getDescription());
        activityDo.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insertActivity(activityDo);
        //保存活动关联的人员信息
        List<CreateUserByActivityParam> activityUserList = param.getActivityUserList();
        List<ActivityUserDo> activityUserDos = activityUserList.stream().map(userParam ->{
            ActivityUserDo activityUserDo = new ActivityUserDo();
            activityUserDo.setActivityId(activityDo.getId());
            activityUserDo.setUserId(userParam.getUserId());
            activityUserDo.setUserName(userParam.getUserName());
            activityUserDo.setStatus(ActivityUserStatusEnum.INIT.name());
            return activityUserDo;
        }).collect(Collectors.toList());

        activityUserMapper.batchInsert(activityUserDos);
        //保存活动关联的奖品信息
        List<CreatePrizeByActivityParam> activityPrizeList = param.getActivityPrizeList();
        List<ActivityPrizeDo> activityPrizeDos = activityPrizeList.stream().map(createPrizeByActivityParam -> {
            ActivityPrizeDo activityPrizeDo = new ActivityPrizeDo();
            activityPrizeDo.setActivityId(activityDo.getId());
            activityPrizeDo.setPrizeId(createPrizeByActivityParam.getPrizeId());
            activityPrizeDo.setPrizeAmount(createPrizeByActivityParam.getPrizeAmount());
            activityPrizeDo.setPrizeTiers(createPrizeByActivityParam.getPrizeTiers());
            activityPrizeDo.setStatus(ActivityPrizeStatusEnum.INIT.name());
            return activityPrizeDo;
        }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDos);

        //整合完整的活动信息存在redis中
        //获取奖品基础信息表
        List<Long> prizeIdList = param.getActivityPrizeList().stream().map(CreatePrizeByActivityParam::getPrizeId)
                .distinct().collect(Collectors.toList());
        List<PrizeDo> prizeDoList = prizeMapper.bachPrizesById(prizeIdList);
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDo,activityUserDos,activityPrizeDos,prizeDoList);
        //缓存
        cacheActivity(activityDetailDTO);
        //构造返回值
        CreateActivityDto Return = new CreateActivityDto();
        Return.setActivityId(activityDetailDTO.getActivityId());


        return Return;
    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        // 获取总量
        int total = activityMapper.count();

        // 获取当前页列表
        List<ActivityDo> activityDOList = activityMapper.selectActivityList(param.offset(), param.getPageSize());
        List<ActivityDTO> activityDTOList = activityDOList.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total, activityDTOList);
    }

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        //查询redis
        if(null == activityId){
            log.warn("查询活动详细信息失败，");
            return null;
        }
        ActivityDetailDTO activityDetailDTO = getActivityFromCache(activityId);
        if(null!=activityDetailDTO){
            log.info("查询活动详细信息成功" +
                    "{}",JacksonUtil.writeValueAsString(activityDetailDTO));
            return activityDetailDTO;
        }

        //redis没查到 查表

        //查活动表
        ActivityDo activityDo = activityMapper.selectById(activityId);

        //活动奖品表
        List <ActivityPrizeDo> activityPrizeDoList = activityPrizeMapper.selectByActivityId(activityId);
        //活动人员表
        List <ActivityUserDo> activityUserDoList = activityUserMapper.selectByActivityId(activityId);
        //奖品表
        //获取奖品id
        List <Long> prizeIds = activityPrizeDoList.stream()
                .map(ActivityPrizeDo::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDo> prizes = prizeMapper.bachPrizesById(prizeIds);
        //整合信息到redis中
        activityDetailDTO = convertToActivityDetailDTO(activityDo, activityUserDoList, activityPrizeDoList, prizes);
        //缓存到redis中
        cacheActivity(activityDetailDTO);

        //返回
        return activityDetailDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if (null == activityId) {
            log.warn("要缓存的活动id为空！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_EMPTY);
        }

        // 查询表数据：活动表、关联奖品、关联人员、奖品信息表
        ActivityDo aDO = activityMapper.selectById(activityId);
        if (null == aDO) {
            log.error("要缓存的活动id有误！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        // 活动奖品表
        List<ActivityPrizeDo> apDOList =  activityPrizeMapper.selectByActivityId(activityId);
        // 活动人员表
        List<ActivityUserDo> auDOList = activityUserMapper.selectByActivityId(activityId);
        // 奖品表: 先获取要查询的奖品id
        List<Long> prizeIds = apDOList.stream()
                .map(ActivityPrizeDo::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDo> pDOList = prizeMapper.bachPrizesById(prizeIds);
        // 整合活动详细信息，存放redis
        cacheActivity(
                convertToActivityDetailDTO(aDO, auDOList,
                        apDOList, pDOList));
    }

    /**
     * 将活动信息缓存到redis中
     * @param activityDetailDTO
     */
    private void cacheActivity(ActivityDetailDTO activityDetailDTO) {
        //key:ACTIVITY_+activityId
        //value:activityDetailDTO的json格式
        if(null==activityDetailDTO || null == activityDetailDTO.getActivityId()){
            log.error("缓存内容缺失");
            return;
        }
        String value = JacksonUtil.writeValueAsString(activityDetailDTO);
        String key = ACTIVITY_HEAD + activityDetailDTO.getActivityId();
        //捕捉异常，以免影响存表
        try {
            redisUtil.set(key,value,ACTIVITY_TIMEOUT);
        }catch (Exception e){
            log.error("缓存错误... activityDetailDTO{}",value,e);
        }

    }

    /**
     * 从缓存中获取活动信息
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if (null == activityId) {
            log.warn("获取缓存活动数据的activityId为空！");
            return null;
        }
        try {
            String str = redisUtil.get(ACTIVITY_HEAD + activityId);
            if (!StringUtils.hasText(str)) {
                log.info("获取的缓存活动数据为空！key={}", ACTIVITY_HEAD + activityId);
                return null;
            }
            return JacksonUtil.readValue(str, ActivityDetailDTO.class);
        } catch (Exception e) {
            log.error("从缓存中获取活动信息异常，key={}", ACTIVITY_HEAD + activityId, e);
            return null;
        }

    }

    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDo activityDo,
                                                         List<ActivityUserDo> activityUserDos,
                                                         List<ActivityPrizeDo> activityPrizeDos, List<PrizeDo> prizeDoList) {
        ActivityDetailDTO returnDto = new ActivityDetailDTO();
        returnDto.setActivityId(activityDo.getId());
        returnDto.setActivityName(activityDo.getActivityName());
        returnDto.setDesc(activityDo.getDescription());
        returnDto.setStatus(ActivityStatusEnum.forName(activityDo.getStatus()));
        /**
         * 设置商品列表
         */
        List<ActivityDetailDTO.PrizeDTO> list1 = activityPrizeDos.stream().map(
                activityPrizeDo -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(activityPrizeDo.getPrizeId());
                    //根据活动奖品关联实体的活动id 过滤出 对应的奖品实体
                    Optional<PrizeDo> optionalPrizeDo = prizeDoList.stream()
                            .filter(prizeDo ->
                                    prizeDo.getId().equals(activityPrizeDo.getPrizeId())
                            ).findFirst();
                    optionalPrizeDo.ifPresent(prizeDo -> {
                        prizeDTO.setName(prizeDo.getName());
                        prizeDTO.setImageUrl(prizeDo.getImageUrl());
                        prizeDTO.setPrice(prizeDo.getPrice());
                        prizeDTO.setDescription(prizeDo.getDescription());
                    });

                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(activityPrizeDo.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(activityPrizeDo.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(activityPrizeDo.getStatus()));

                    return prizeDTO;
                }
        ).collect(Collectors.toList());
        returnDto.setPrizeDTOList(list1);

        /**
         * 设置人员列表
         */
        List<ActivityDetailDTO.UserDTO> list2 = activityUserDos.stream()
                        .map(activityUserDo -> {
                            ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                            userDTO.setUserId(activityUserDo.getUserId());
                            userDTO.setUserName(activityUserDo.getUserName());
                            userDTO.setStatus(ActivityUserStatusEnum.forName(activityUserDo.getStatus()));
                            return userDTO;
                        }).collect(Collectors.toList());
        returnDto.setUserDTOList(list2);
        return returnDto;
    }

    /**
     * 校验活动有效性
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if(null == param){
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_EMPTY);
        }
        //校验人员id是否存在
        List<Long> userIds = param.getActivityUserList().stream()
                .map(CreateUserByActivityParam::getUserId).distinct().collect(Collectors.toList());
        List<Long> exitsUserIds = userMapper.selectExistByIds(userIds);
        for(Long id:userIds){
            if(!exitsUserIds.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        }
        //校验奖品id是否存在
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId).distinct().collect(Collectors.toList());
        List<Long> exitsPrizeId = prizeMapper.selectPrizesById(prizeIds);
        for(Long id:prizeIds){
            if(!exitsPrizeId.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        }
        //人员数量大于等于奖品数量
        int userCount = param.getActivityUserList().size();
        int prizeCount = 0;
        for (CreatePrizeByActivityParam param1:param.getActivityPrizeList()){
            prizeCount += param1.getPrizeAmount();
        }
        if(userCount<prizeCount){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_PRIZE_ERROR);
        }

        //检验奖品等级是否合法
        List<CreatePrizeByActivityParam> activityPrizeList = param.getActivityPrizeList();
        for(CreatePrizeByActivityParam param1:activityPrizeList){
            if(null == ActivityPrizeTiersEnum.forName(param1.getPrizeTiers())){
                throw new ServiceException(ServiceErrorCodeConstants.PRIZE_TITERS_ERROR);
            }
        }
    }
}
